#include "CLComList.h"
#include "clcomidfactory.h"
CLComList::CLComList()
{
	m_pHeader = NULL;
	m_pTailer = NULL;
}

CLComList::~CLComList()
{
	Clear();
}
void CLComList::Clear()
{
	m_pHeader = NULL;
	m_pTailer = NULL;

    map<CLComString, CListItem*>::iterator it =
		m_mapListItems.begin();
	for (; it != m_mapListItems.end(); ++it)
	{
		if (m_pReleasecb)
		{
			m_pReleasecb(it->second->GetPointerValue());
		}
		delete it->second;
		it->second = nullptr;
	}
	m_mapListItems.clear();
}

void CLComList::Copy(CLComList* pdst, const CLComList* psrc)
{
	pdst->Clear();

    CLComString itemid = psrc->GetFirstItem();

	if (itemid.GetLength() > 0)
	{
		do 
		{
            CLComValue itemvalue;
            CLComValue* pItem = psrc->GetItem(itemid)->GetValue();

			itemvalue.DeepCopy(*pItem);
			pdst->InsertBack(itemid, itemvalue);

			itemid = psrc->GetNextItem(itemid);
		} while (itemid.GetLength() > 0);
	}
}
void CLComList::SetReleaseCallback(
	PITEMRELEASECALLBACK rcb)
{
	m_pReleasecb = rcb;
}


void CLComList::InsertItemBack(CListItem* pItem,
	CListItem* ppre)
{
	if (!m_pHeader)
	{
		m_pHeader = pItem;

		m_pTailer = pItem;
	}
	else if (!ppre || m_pTailer == ppre)
	{
		m_pTailer->SetNextItem(pItem);
		pItem->SetPreItem(m_pTailer);

		m_pTailer = pItem;
	}
	else
	{
		CListItem* poldNext = ppre->GetNextItem();

		pItem->SetNextItem(poldNext);
		poldNext->SetPreItem(pItem);

		pItem->SetPreItem(ppre);
		ppre->SetNextItem(pItem);
	}
}

void CLComList::InsertItemFront(CListItem* pItem,
	CListItem* pNext)
{
	if (!m_pHeader)
	{
		m_pHeader = pItem;

		m_pTailer = pItem;
	}
	else if (!pNext || m_pHeader == pNext)
	{
		pItem->SetNextItem(m_pHeader);
		m_pHeader->SetPreItem(pItem);

		m_pHeader = pItem;
	}
	else
	{
		CListItem* poldPre = pNext->GetPreItem();

		pItem->SetNextItem(pNext);
		pNext->SetPreItem(pItem);

		poldPre->SetNextItem(pItem);
		pItem->SetPreItem(poldPre);
	}
}


void CLComList::InsertBack(CLComString id,
    const CLComValue& invalue,
    CLComString preID)
{
	InsertItemBack(id, invalue, GetItem(preID));
}
CLComString CLComList::InsertBack(
    const CLComValue& invalue,
    CLComString preID)
{
    CLComString id = CLComIDFactory::CreateID("L");

    InsertItemBack(id, invalue, GetItem(preID));

    return id;
}

void CLComList::InsertFront(CLComString id,
    const CLComValue& invalue,
    CLComString nextID)
{
	InsertItemFront(id, invalue, GetItem(nextID));
}
CLComString CLComList::InsertFront(
    const CLComValue& invalue,
    CLComString nextID)
{
    CLComString id = CLComIDFactory::CreateID("L");
    InsertItemFront(id, invalue, GetItem(nextID));
    return id;
}


void CLComList::InsertItemBack(CLComString id, const CLComValue& invalue,
	CListItem* pPre)
{
	EnterSection();

	if (GetItem(id))
	{
		LeaveSection();
		return;
	}

	CListItem* pItem =
		new CListItem(id, invalue,
			(int)m_mapListItems.size());

	m_mapListItems[id] = pItem;

	InsertItemBack(pItem, pPre);

	RecalIndex(pItem);

	LeaveSection();
}


void CLComList::InsertItemFront(CLComString id, const CLComValue& invalue,
	CListItem* pNext)
{
	EnterSection();
	if (GetItem(id))
	{
		LeaveSection();
		return;
	}

	CListItem* pItem =
		new CListItem(id, invalue,
		(int)m_mapListItems.size());

	m_mapListItems[id] = pItem;

	InsertItemFront(pItem, pNext);

	RecalIndex(pItem);

	LeaveSection();
}


void CLComList::EraseItem(CListItem*& pItem)
{
	if (!pItem)
	{
		return;
	}
	EnterSection();

	DelRelateItem(pItem);

	m_mapIndexs.erase(pItem->GetIndex());

	if (m_pReleasecb)
	{
		m_pReleasecb(pItem->GetPointerValue());
	}

	m_mapListItems.erase(pItem->GetKey());

	delete pItem;
	pItem = nullptr;

	LeaveSection();
}


void CLComList::EraseItem(CLComString id)
{
	CListItem* pItem = GetItem(id);
	EraseItem(pItem);
}


void CLComList::EraseItem(vector<CLComString>& ids)
{
    for (size_t i = 0; i < ids.size(); ++i)
	{
		EraseItem(ids[i]);
	}
}


void CLComList::EraseItem(CLComString startID,
    CLComString endID)
{
	CListItem* pStart = NULL;
	CListItem* pEnd = NULL;

	if (IsSerial(startID, endID, pStart, pEnd))
	{
		if (pStart && pEnd)
		{
			if (pStart->GetIndex() > pEnd->GetIndex())
			{
				return;
			}
		}
		if (!pStart && !pEnd)
		{
			return;
		}
		if (!pStart)
		{
			pStart = m_pHeader;
		}
		if (!pEnd)
		{
			pEnd = m_pTailer;
		}

		CListItem* pItem = pStart;
		while (pItem)
		{
			CListItem* pNextDel =
				pItem->GetNextItem();

			if (pNextDel == pEnd)
			{
				pNextDel = NULL;
			}

			EraseItem(pItem);

			pItem = pNextDel;
		}

	}
}


void CLComList::RecalIndex(CListItem* pItem)
{
	if (!pItem)
	{
		return;
	}
	CListItem* ppre = pItem->GetPreItem();
	if (!ppre)
	{
		pItem->SetIndex(0);
		m_mapIndexs[0] = pItem;
	}
	else
	{
		int nPreIndex = ppre->GetIndex();

		pItem->SetIndex(++nPreIndex);

		m_mapIndexs[nPreIndex] = pItem;
	}
	CListItem* pNext = pItem->GetNextItem();
	if (pNext)
	{
		RecalIndex(pNext);
	}
}

CLComString CLComList::GetFirstItem()const
{
	if (m_pHeader)
	{
		return m_pHeader->GetKey();
	}
	return "";
}

CLComString CLComList::GetNextItem(CLComString id)const
{
	CListItem* pItem = GetItem(id);
	if (pItem)
	{
		CListItem* pNext = pItem->GetNextItem();

		if (pNext)
		{
			return pNext->GetKey();
		}
	}
	return "";
}


CLComString CLComList::GetPreItem(CLComString id)const
{
	CListItem* pItem = GetItem(id);
	if (pItem)
	{
		CListItem* pPre = pItem->GetPreItem();
		if (pPre)
		{
			return pPre->GetKey();
		}
	}
	return "";
}
size_t CLComList::GetSize()const
{
    return m_mapListItems.size();
}

void CLComList::DelRelateItem(CListItem* pItem)
{
	if (!pItem)
	{
		return;
	}

	CListItem* pPre = pItem->GetPreItem();
	CListItem* pNext = pItem->GetNextItem();

	if (pPre)
	{
		pPre->SetNextItem(pNext);

		if (!pNext)
		{
			m_pTailer = pPre;
		}
	}
	else
	{
		if (pNext)
		{
			m_pHeader = pNext;
		}
		else
		{
			m_pTailer = NULL;
			m_pHeader = NULL;
		}
	}
	if (pNext)
	{
		pNext->SetPreItem(pPre);
	}
	pItem->SetPreItem(NULL);
	pItem->SetNextItem(NULL);
}


int CLComList::MoveUp(CListItem* pItem)
{
	if (pItem)
	{
		CListItem* pPreItem =
			pItem->GetPreItem();
		if (pPreItem)
		{
			DelRelateItem(pItem);

			InsertItemFront(pItem, pPreItem);
			return ECLIST_RETURN_VALUE_OK;
		}
	}
	return ECLIST_RETURN_VALUE_FALSE;
}


int CLComList::MoveDown(CListItem* pItem)
{
	if (pItem)
	{
		CListItem* pNext =
			pItem->GetNextItem();
		if (pNext)
		{
			DelRelateItem(pItem);

			InsertItemBack(pItem, pNext);
			return ECLIST_RETURN_VALUE_OK;
		}
	}
	return ECLIST_RETURN_VALUE_FALSE;
}


int CLComList::MoveUp(CLComString currentid)
{
	return MoveUp(GetItem(currentid));
}


int CLComList::MoveDown(CLComString currentid)
{
	return MoveDown(GetItem(currentid));
}


CListItem* CLComList::GetItem(CLComString id)const
{
    map<CLComString, CListItem*>::const_iterator it =
		m_mapListItems.find(id);

	if (it != m_mapListItems.end())
	{
		return it->second;
	}
	return NULL;
}


bool CLComList::IsSerial(CLComString startID,
    CLComString endID,
	CListItem*& pStartItem,
	CListItem*& pEndItem)
{
	pStartItem = GetItem(startID);
	pEndItem = GetItem(endID);

	if (!pStartItem || !pEndItem)
	{
		return false;
	}
	if (pStartItem->GetIndex() <= pEndItem->GetIndex())
	{
		return true;
	}
	return false;
}


bool CLComList::CalcSerialItems(CLComString startID,
    CLComString endID,
	CListItem*& pStartItem,
	CListItem*& pEndItem)
{
	pStartItem = NULL;
	pEndItem = NULL;

	if (IsSerial(startID, endID, pStartItem, pEndItem))
	{
		if (pStartItem && pEndItem)
		{
			if (pStartItem->GetIndex() > pEndItem->GetIndex())
			{
				return false;
			}
		}
		if (!pStartItem && !pEndItem)
		{
			return false;
		}
		if (!pStartItem)
		{
			pStartItem = m_pHeader;
		}
		if (!pEndItem)
		{
			pEndItem = m_pTailer;
		}
		return true;
	}
	return false;
}



void CLComList::Move2Next(CListItem* pPosItem,
	CListItem* pItem)
{
	if (pPosItem && pItem)
	{
		DelRelateItem(pItem);

		InsertItemBack(pItem, pPosItem);
	}
}



void CLComList::Move2Front(CListItem* pPosItem,
	CListItem* pItem)
{
	if (pPosItem && pItem)
	{
		DelRelateItem(pItem);

		InsertItemFront(pItem, pPosItem);
	}
}


void CLComList::Move2Next(CLComString frontID,
    CLComString currentid)
{
	Move2Next(GetItem(frontID), GetItem(currentid));
}


void CLComList::Move2Front(CLComString nextID,
    CLComString currentid)
{
	Move2Front(GetItem(nextID), GetItem(currentid));
}


void CLComList::Move(CLComString startID,
    CLComString endID, EMOVE_TYPE moveType)
{
	CListItem* pStart = NULL;
	CListItem* pEnd = NULL;

	if (CalcSerialItems(startID, endID, pStart, pEnd))
	{
		CListItem* pItem = pStart;
		while (pItem)
		{
			CListItem* pNextDel =
				pItem->GetNextItem();

			if (pNextDel == pEnd)
			{
				pNextDel = NULL;
			}

			switch (moveType)
			{
			case EMOVE_TYPE_UP:
				MoveUp(pItem);
				break;
			case EMOVE_TYPE_DOWN:
				MoveDown(pItem);
				break;
			default:
				break;
			}

			pItem = pNextDel;
		}
	}
}


void CLComList::Move2(CLComString PosID,
    CLComString startID, CLComString endID,
	EMOVE_TYPE moveType)
{
	CListItem* pStart = NULL;
	CListItem* pEnd = NULL;
	CListItem* pPosItem = GetItem(PosID);

	if (pPosItem && CalcSerialItems(startID,
		endID, pStart, pEnd))
	{
		CListItem* pItem = pStart;
		while (pItem)
		{
			CListItem* pNextDel =
				pItem->GetNextItem();

			if (pNextDel == pEnd)
			{
				pNextDel = NULL;
			}

			switch (moveType)
			{
			case EMOVE_TYPE_UP:
				Move2Front(pPosItem, pItem);
				break;
			case EMOVE_TYPE_DOWN:
				Move2Next(pPosItem, pItem);
				break;
			default:
				break;
			}

			pItem = pNextDel;
		}
	}
}
